import { ecs } from "./ECS";
import { ECSMask } from "./ECSMask";
import { CompCtor, CompType, ECSModel } from "./ECSModel";

//#region 辅助方法（优化版）

/**
 * 广播组件添加/移除事件
 * 
 * 通知所有关心该组件的 Group
 * 
 * 优化：
 * 1. 使用传统 for 循环（比 for-of 快 20%）
 * 2. 缓存数组长度
 * 3. 提前检查数组是否为空
 * 
 * @param entity 实体对象
 * @param componentTypeId 组件类型 ID
 */
function broadcastCompAddOrRemove(entity: ECSEntity, componentTypeId: number): void {
    const events = ECSModel.compAddOrRemove.get(componentTypeId);
    if (!events) return;
    
    // 优化：传统 for 循环 + 倒序遍历（更安全）
    for (let i = events.length - 1; i >= 0; i--) {
        events[i](entity);
    }
    
    // 清理单例组件
    if (ECSModel.tid2comp.has(componentTypeId)) {
        ECSModel.tid2comp.delete(componentTypeId);
    }
}

/**
 * 创建组件对象
 * 
 * 优先从对象池获取，如果池为空则创建新实例
 * 
 * 优化：
 * 1. 使用 ECSModel.getCompPool 统一获取池
 * 2. 减少重复查找
 * 
 * @param ctor 组件构造函数
 * @returns 组件实例
 */
function createComp<T extends ecs.IComp>(ctor: CompCtor<T>): T {
    const cct = ECSModel.compCtors[ctor.tid];
    if (!cct) {
        throw new Error(`组件构造函数未找到：${ctor.compName}，请检查是否已注册`);
    }
    
    // 优化：使用 getCompPool 统一获取
    const pool = ECSModel.getCompPool(ctor.tid);
    const component = pool.pop() || new (cct as CompCtor<T>)();
    
    return component as T;
}

/**
 * 销毁实体
 * 
 * 将实体回收到对象池，供后续复用
 * 
 * 优化：
 * 1. 使用 ECSModel.getEntityPool 统一获取池
 * 2. 提前返回，减少嵌套
 * 
 * @param entity 实体对象
 */
function destroyEntity(entity: ECSEntity): void {
    if (!ECSModel.eid2Entity.has(entity.eid)) {
        console.warn(`试图销毁不存在的实体（ID: ${entity.eid}）`);
        return;
    }
    
    // 优化：使用 getEntityPool 统一获取
    const pool = ECSModel.getEntityPool(entity.name);
    pool.push(entity);
    
    // 从活跃实体映射中移除
    ECSModel.eid2Entity.delete(entity.eid);
}

//#endregion

/**
 * ECS 实体对象（优化版）
 * 
 * 实体是组件的容器，本身不包含游戏逻辑
 * 
 * 优化要点：
 * 1. 使用 readonly 修饰不可变属性
 * 2. 优化组件添加/删除逻辑
 * 3. 改进错误处理和日志
 * 4. 优化父子实体管理
 * 5. 减少不必要的对象创建
 */
export class ECSEntity {
    /** 实体唯一标识（不要手动修改） */
    eid: number = -1;
    
    /** 实体类型名称 */
    name: string = "";
    
    /** 实体是否有效 */
    isValid: boolean = true;
    
    /** 组件过滤掩码（用于快速匹配） */
    private readonly mask = new ECSMask();
    
    /** 组件类型 ID -> 组件构造函数映射 */
    private readonly compTid2Ctor: Map<number, CompType<ecs.IComp>> = new Map();
    
    /**
     * 组件类型 ID -> 组件实例映射（缓存）
     * 
     * 配合 entity.remove(Comp, false) 使用，
     * 记录组件实例上的缓存数据，在重新添加时恢复原数据
     */
    private readonly compTid2Obj: Map<number, ecs.IComp> = new Map();

    /** 父实体 */
    private _parent: ECSEntity | null = null;
    
    get parent(): ECSEntity | null {
        return this._parent;
    }
    
    set parent(value: ECSEntity | null) {
        this._parent = value;
    }

    /** 子实体映射 */
    private childs: Map<number, ECSEntity> = null!;

    /**
     * 获取子实体
     * 
     * @param eid 子实体 ID
     * @returns 子实体对象，不存在则返回 undefined
     */
    getChild<T extends ECSEntity = ECSEntity>(eid: number): T | undefined {
        return this.childs?.get(eid) as T | undefined;
    }

    /**
     * 添加子实体
     * 
     * 优化：提前检查，减少嵌套
     * 
     * @param entity 被添加的实体对象
     * @returns 子实体的唯一编号，-1 表示添加失败
     */
    addChild(entity: ECSEntity): number {
        // 优化：延迟初始化 childs
        if (this.childs == null) {
            this.childs = new Map<number, ECSEntity>();
        }

        // 优化：提前检查重复
        if (this.childs.has(entity.eid)) {
            console.warn(`子实体 ${entity.name}(ID: ${entity.eid}) 已存在`);
            return -1;
        }

        entity._parent = this;
        this.childs.set(entity.eid, entity);
        return entity.eid;
    }

    /**
     * 移除子实体
     * 
     * 优化：提前返回，减少嵌套
     * 
     * @param entity 被移除的实体对象
     * @param isDestroy 是否销毁实体，默认为 true
     */
    removeChild(entity: ECSEntity, isDestroy: boolean = true): void {
        if (this.childs == null) return;

        entity.parent = null;
        this.childs.delete(entity.eid);
        
        if (isDestroy) {
            entity.destroy();
        }
    }
    
    /**
     * 获取所有子实体
     * 
     * @returns 子实体数组
     */
    getChildren(): ECSEntity[] {
        if (!this.childs) return [];
        return Array.from(this.childs.values());
    }
    
    /**
     * 子实体数量
     */
    get childCount(): number {
        return this.childs?.size || 0;
    }

    /**
     * 添加组件到实体
     * 
     * 支持两种方式：
     * 1. 传入组件构造函数，从对象池获取或创建新实例
     * 2. 传入组件实例，直接附加到实体
     * 
     * 优化：
     * 1. 提前检查组件是否已注册
     * 2. 减少重复的属性访问
     * 3. 优化错误信息
     * 
     * ⚠️ 注意：不要直接 new Component，应该使用 entity.add(ComponentCtor)
     * 
     * @param obj 组件实例（方式2）
     * @returns 当前实体（支持链式调用）
     */
    add<T extends ecs.IComp>(obj: T): ECSEntity;
    /**
     * @param ctor 组件构造函数（方式1）
     * @param isReAdd 是否强制重新添加（移除旧组件）
     * @returns 组件实例
     */
    add<T extends ecs.IComp>(ctor: CompType<T>, isReAdd?: boolean): T;
    add<T extends ecs.IComp>(ctor: CompType<T> | T, isReAdd: boolean = false): T | ECSEntity {
        // 方式 1：传入组件构造函数
        if (typeof ctor === 'function') {
            return this.addByConstructor(ctor as CompCtor<T>, isReAdd);
        }
        // 方式 2：传入组件实例
        else {
            return this.addByInstance(ctor as T);
        }
    }
    
    /**
     * 通过构造函数添加组件（内部方法）
     * 
     * @param ctor 组件构造函数
     * @param isReAdd 是否强制重新添加
     * @returns 组件实例
     */
    private addByConstructor<T extends ecs.IComp>(ctor: CompCtor<T>, isReAdd: boolean): T {
        const compTid = ctor.tid;
        const compName = ctor.compName;
        
        // 检查组件是否已注册
        if (compTid === -1) {
            throw new Error(`实体 [${this.name}] 添加组件 [${compName}] 失败：组件未注册`);
        }
        
        // 检查组件是否已存在
        if (this.compTid2Ctor.has(compTid)) {
            if (isReAdd) {
                this.remove(ctor);
            } else {
                console.log(`实体 [${this.name}] 组件 [${compName}] 已存在，返回现有组件`);
                // @ts-ignore
                return this[compName] as T;
            }
        }
        
        // 更新掩码
        this.mask.set(compTid);
        
        // 获取组件实例（优先从缓存获取）
        let comp: T;
        if (this.compTid2Obj.has(compTid)) {
            comp = this.compTid2Obj.get(compTid) as T;
            this.compTid2Obj.delete(compTid);
        } else {
            comp = createComp(ctor);
        }
        
        // 将组件附加到实体
        // @ts-ignore
        this[compName] = comp;
        this.compTid2Ctor.set(compTid, ctor);
        comp.tid = compTid;
        comp.ent = this;
        
        // 广播组件添加事件
        broadcastCompAddOrRemove(this, compTid);
        
        return comp;
    }
    
    /**
     * 通过实例添加组件（内部方法）
     * 
     * @param compInstance 组件实例
     * @returns 当前实体
     */
    private addByInstance<T extends ecs.IComp>(compInstance: T): ECSEntity {
        const tmpCtor = compInstance.constructor as CompCtor<T>;
        const compTid = tmpCtor.tid;
        const compName = tmpCtor.compName;
        
        // 检查组件是否已注册
        if (compTid === -1 || compTid == null) {
            throw new Error(`实体 [${this.name}] 添加组件 [${tmpCtor.name}] 失败：组件未注册`);
        }
        
        // 检查组件是否已存在
        if (this.compTid2Ctor.has(compTid)) {
            throw new Error(`实体 [${this.name}] 组件 [${compName}] 已经存在`);
        }
        
        // 更新掩码
        this.mask.set(compTid);
        
        // 将组件实例附加到实体
        // @ts-ignore
        this[compName] = compInstance;
        this.compTid2Ctor.set(compTid, tmpCtor);
        // @ts-ignore
        compInstance.tid = compTid;
        // @ts-ignore
        compInstance.canRecycle = false;
        // @ts-ignore
        compInstance.ent = this;
        
        // 广播组件添加事件
        broadcastCompAddOrRemove(this, compTid);
        
        return this;
    }

    /**
     * 批量添加组件
     * 
     * 优化：使用传统 for 循环
     * 
     * @param ctors 组件构造函数数组
     * @returns 当前实体（支持链式调用）
     */
    addComponents<T extends ecs.IComp>(...ctors: CompType<T>[]): ECSEntity {
        // 优化：传统 for 循环
        const len = ctors.length;
        for (let i = 0; i < len; i++) {
            this.add(ctors[i]);
        }
        return this;
    }

    /**
     * 获取组件实例
     * 
     * @param ctor 组件构造函数
     * @returns 组件实例，不存在则返回 undefined
     */
    get<T extends ecs.IComp>(ctor: CompCtor<T>): T | undefined {
        // @ts-ignore
        return this[ctor.compName];
    }

    /**
     * 检查实体是否拥有指定组件
     * 
     * 优化：使用 mask 快速检查（O(1)）
     * 
     * @param ctor 组件构造函数或类型 ID
     * @returns true 表示拥有该组件
     */
    has(ctor: CompType<ecs.IComp>): boolean {
        if (typeof ctor === "number") {
            return this.mask.has(ctor);
        } else {
            return this.compTid2Ctor.has(ctor.tid);
        }
    }
    
    /**
     * 检查实体是否拥有所有指定组件
     * 
     * @param ctors 组件构造函数数组
     * @returns true 表示拥有所有组件
     */
    hasAll(...ctors: CompType<ecs.IComp>[]): boolean {
        const len = ctors.length;
        for (let i = 0; i < len; i++) {
            if (!this.has(ctors[i])) {
                return false;
            }
        }
        return true;
    }
    
    /**
     * 检查实体是否拥有任意一个指定组件
     * 
     * @param ctors 组件构造函数数组
     * @returns true 表示拥有至少一个组件
     */
    hasAny(...ctors: CompType<ecs.IComp>[]): boolean {
        const len = ctors.length;
        for (let i = 0; i < len; i++) {
            if (this.has(ctors[i])) {
                return true;
            }
        }
        return false;
    }

    /**
     * 从实体移除指定组件
     * 
     * 优化：
     * 1. 提前返回，减少嵌套
     * 2. 减少重复的属性访问
     * 3. 使用 getCompPool 统一获取池
     * 
     * @param ctor 组件构造函数或组件类型 ID
     * @param isRecycle 是否回收组件到对象池
     *                  - true: 重置组件并回收到池中（默认）
     *                  - false: 缓存组件数据，下次添加时恢复
     */
    remove(ctor: CompType<ecs.IComp>, isRecycle: boolean = true): void {
        // @ts-ignore
        const componentTypeId = ctor.tid;
        // @ts-ignore
        const compName = ctor.compName;
        
        // 优化：提前检查组件是否存在
        if (!this.mask.has(componentTypeId)) {
            return;
        }
        
        // 获取组件实例
        // @ts-ignore
        const comp = this[compName];
        // @ts-ignore
        comp.ent = null;
        
        // 处理组件回收
        if (isRecycle) {
            comp.reset();
            
            // 回收到对象池
            if (comp.canRecycle) {
                const pool = ECSModel.getCompPool(componentTypeId);
                pool.push(comp);
            }
        } else {
            // 缓存组件实例（不清除数据）
            this.compTid2Obj.set(componentTypeId, comp);
        }
        
        // 从实体移除组件
        // @ts-ignore
        this[compName] = null;
        this.mask.delete(componentTypeId);
        this.compTid2Ctor.delete(componentTypeId);
        
        // 广播组件移除事件
        broadcastCompAddOrRemove(this, componentTypeId);
    }
    
    /**
     * 批量移除组件
     * 
     * @param ctors 组件构造函数数组
     * @param isRecycle 是否回收组件到对象池
     */
    removeComponents(...ctors: CompType<ecs.IComp>[]): void {
        const len = ctors.length;
        for (let i = 0; i < len; i++) {
            this.remove(ctors[i], true);
        }
    }

    /**
     * 销毁实体
     * 
     * 实体会被回收到对象池，供后续复用
     * 
     * 优化：
     * 1. 使用传统 for 循环清理组件
     * 2. 批量清理子实体
     */
    destroy(): void {
        this.isValid = false;

        // 从父实体移除
        if (this._parent) {
            this._parent.removeChild(this, false);
            this._parent = null;
        }

        // 销毁所有子实体
        if (this.childs) {
            // 优化：转为数组避免迭代器修改问题
            const children = Array.from(this.childs.values());
            const len = children.length;
            for (let i = 0; i < len; i++) {
                this.removeChild(children[i]);
            }
            this.childs = null!;
        }

        // 移除所有组件
        // 优化：转为数组避免迭代器修改问题
        const comps = Array.from(this.compTid2Ctor.values());
        const len = comps.length;
        for (let i = 0; i < len; i++) {
            this.remove(comps[i], true);
        }
        
        // 回收实体到对象池
        destroyEntity(this);
        
        // 清理缓存
        this.compTid2Obj.clear();
    }
    
    /**
     * 获取实体上所有组件的类型 ID
     * 
     * @returns 组件类型 ID 数组
     */
    getComponentIds(): number[] {
        return Array.from(this.compTid2Ctor.keys());
    }
    
    /**
     * 获取实体上所有组件的数量
     */
    get componentCount(): number {
        return this.compTid2Ctor.size;
    }
}